﻿
using Xacml.Core.Interfaces;
using Xacml.Core.Policy;

namespace Xacml.Core.Runtime
{
  public class Condition : ApplyBase, IEvaluable
  {
    public Condition(ConditionElement condition)
      : base((ApplyBaseReadWrite) condition)
    {
    }

    public override EvaluationValue Evaluate(EvaluationContext context)
    {
      EvaluationValue evaluationValue = (EvaluationValue) null;
      context.Trace("Evaluating condition...");
      context.AddIndent();
      try
      {
        IFunction function = context.Engine.GetFunction(this.ApplyDefinition.FunctionId);
        if (function == null)
        {
          context.Trace("ERR: function not found {0}", (object) this.ApplyDefinition.FunctionId);
          context.ProcessingError = true;
          return EvaluationValue.Indeterminate;
        }
        if (function.Returns == null)
        {
          context.Trace("The function '{0}' does not defines it's return value", (object) this.ApplyDefinition.FunctionId);
          evaluationValue = EvaluationValue.Indeterminate;
          context.ProcessingError = true;
        }
        else if (function.Returns != DataTypeDescriptor.Boolean)
        {
          context.Trace("Function does not return Boolean a value");
          evaluationValue = EvaluationValue.Indeterminate;
          context.ProcessingError = true;
        }
        else
          evaluationValue = base.Evaluate(context);
        if (evaluationValue.IsIndeterminate)
        {
          context.Trace("condition evaluated into {0}", (object) evaluationValue.ToString());
          return evaluationValue;
        }
        if (!(evaluationValue.Value is bool))
        {
          context.Trace("condition evaluated into {0}", (object) evaluationValue.ToString());
          return EvaluationValue.Indeterminate;
        }
        if (evaluationValue.BoolValue)
        {
          context.Trace("condition evaluated into {0}", (object) evaluationValue.ToString());
          return EvaluationValue.True;
        }
        if (context.IsMissingAttribute)
        {
          context.Trace("condition evaluated into {0}", (object) evaluationValue.ToString());
          return EvaluationValue.Indeterminate;
        }
        context.Trace("condition evaluated into {0}", (object) evaluationValue.ToString());
        return EvaluationValue.False;
      }
      finally
      {
        context.TraceContextValues();
        context.RemoveIndent();
        context.Trace("Condition: {0}", (object) evaluationValue.ToString());
      }
    }
  }
}
